class Food {
    element: HTMLElement = document.querySelector('.food')!;
    size: number = 10;

    // 获取食物X轴
    get x() {
        return this.element.offsetLeft;
    }

    // 获取食物Y轴
    get y() {
        return this.element.offsetTop;
    }

    // 随机修改食物位置
    modifyLocation() {
        let box: HTMLElement = document.querySelector('.box')!; //获取box盒子元素
        // 随机位置要求要在 10的倍数内出现。等价于Math.round(Math.random() * 屏幕所占大小-食物大小/10)*10
        this.element.style.left = Math.round(Math.random() * (box.clientWidth - 10) / 10) * 10 + 'px';
        this.element.style.top = Math.round(Math.random() * (box.clientHeight - 10) / 10) * 10 + 'px';
        //region 测试生成的随机数是否符合要求
        // let arr:number[] =[];
        // for (let i = 0; i < 1000; i++) {
        //     arr[i] = Math.round(Math.random() * (box.clientHeight-10) / 10) * 10;
        // }
        // arr.forEach((value, index) => {
        //     if (value < 0 || value > 290) {
        //         console.log(false);
        //     }else {
        //         console.log(true);
        //     }
        // })
        //endregion

    }
}

class Detail {
    score: number = 0;
    level: number = 1;
    maxLevel: number = 10; //最大等级限制
    scoreEle: HTMLElement = document.querySelector('.score>span')!;
    levelEle: HTMLElement = document.querySelector('.level>span')!;

    // 增加分数
    scoreUp() {
        this.scoreEle.innerHTML = ++this.score + '';
        if (this.score % 10 === 0) this.levelUp(); //每获得十分会提升一个等级
    }

    // 增加等级
    levelUp() {
        if (this.level < this.maxLevel) this.levelEle.innerHTML = ++this.level + ''; //小于等级限制则增加
    }
}

class Snake {
    snakeEle: HTMLElement = document.querySelector('.snake')!;
    head: HTMLElement = document.querySelector('.head')!;
    snakeWhole: HTMLCollectionOf<HTMLElement> = this.snakeEle.getElementsByTagName('div')!;
    live: boolean = true;


//     获取蛇头坐标
    get x() {
        return this.head.offsetLeft;
    }

    get y() {
        return this.head.offsetTop;
    }

    set x(value: number) {
        if (value === this.x) return; //原值相改同不做修
        if (value <= box.width - food.size && value >= 0) {
            // 判断蛇头下一步移动的位置是否等于第一节身体的位置，如果相等则表示掉头
            if (this.snakeWhole[1] && (this.snakeWhole[1] as HTMLElement).offsetLeft === value) {

                if (value > this.x) { //处理向右转头
                    value = this.x-10;
                } else {  //处理向左转头
                    value = this.x+10;
                }
            }
            this.moveBody();
            this.head.style.left = value + 'px';
            gameControl.checkHeadBody();

        } else {
            throw new Error('游戏结束！！！');
        }

    }

    set y(value: number) {
        if (value === this.y) return; //原值相改同不做修
        if (value <= box.height - food.size && value >= 0) {
            if (this.snakeWhole[1] && (this.snakeWhole[1] as HTMLElement).offsetTop === value) {
                if (value > this.y) { //处理向下转头
                    value = this.y-10;
                } else {  //处理向上转头
                    value = this.y+10;
                }
            }
            this.moveBody();
            this.head.style.top = value + 'px';
            gameControl.checkHeadBody();

        } else {
            throw new Error('游戏结束！！！');
        }
    }

    addBody() {
        this.snakeEle.insertAdjacentHTML("beforeend", "<div class='bodies' ></div>");
        console.log(this.snakeWhole)
    }

    moveBody() {
        for (let i = this.snakeWhole.length - 1; i > 0; i--) {
            let x = (this.snakeWhole[i - 1] as HTMLElement).offsetLeft;
            let y = (this.snakeWhole[i - 1] as HTMLElement).offsetTop;
            (this.snakeWhole[i] as HTMLElement).style.left = x + 'px';
            (this.snakeWhole[i] as HTMLElement).style.top = y + 'px';
        }
    }
}

class Box {
    element: HTMLElement = document.querySelector('.box')!;
    width: number = this.element.clientWidth;
    height: number = this.element.clientHeight;
}

class GameControl {
    snake: Snake = new Snake();
    food: Food = new Food();
    detail: Detail = new Detail();
    direction: string = 'ArrowRight';


//     游戏初始化方法
    init() {
        document.addEventListener('keydown', this.keyDownHandler.bind(this))

        this.moveHead();
    }

//     处理键盘响应事件 ArrowUp ArrowDown ArrowLeft ArrowRight
    keyDownHandler(e: KeyboardEvent) {
        let trueKey = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight']
        if (trueKey.includes(e.key)) {
            this.direction = e.key;
        }

    }

//     移动
    moveHead() {
        // 判断蛇的移动是否撞墙，移动范围在 0~290 盒子大小减去食物大小
        let x = snake.x;
        let y = snake.y;
        try {
            switch (this.direction) {
                case 'ArrowUp':
                    y -= 10;
                    break;
                case 'ArrowDown':
                    y += 10;
                    break;
                case 'ArrowLeft':
                    x -= 10;
                    break;
                case 'ArrowRight':
                    x += 10;
                    break;
            }
            gameControl.checkEat(x, y) //判断是否吃到食物
            snake.x = x;
            snake.y = y;
        } catch (e) {
            alert((e as Error).message);
            snake.live = false;
        }
        // 先判断蛇是否存活 根据关卡等级，移动速度每升一级速度加快10%
        snake.live && setTimeout(this.moveHead.bind(this), 300 - (this.detail.level - 1) * 30);

    }

    // 检测是否吃到食物
    checkEat(x: number, y: number) {
        if (x === food.x && y === food.y) {
            food.modifyLocation();
            detail.scoreUp();
            snake.addBody();
        }
    }

//     检测头和身体是否相撞
    checkHeadBody(){
        for (let i = 1; i < snake.snakeWhole.length; i++) {
            if (snake.head.offsetLeft === snake.snakeWhole[i].offsetLeft && snake.head.offsetTop === snake.snakeWhole[i].offsetTop) {
                throw Error('游戏结束！！！');
            }
        }
    }

}


//region 实例化对象
let food = new Food();
let detail = new Detail();
let snake = new Snake();
let box = new Box();
let gameControl = new GameControl();

//endregion

food.modifyLocation();
gameControl.init();




